﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.IO;

namespace DocumentsTagger.BL
{
    internal delegate void FoundFileHandler(string PathToFile);

    internal class DirectoriesScanner
    {
        private const int MAX_THREADS = 5;
        private const int SECOND = 1000;
        private const int MINUTE = 60 * 1000;
        private const int SLEEP_TIME = 5 * SECOND;

        private static string[] s_AllowedExtensions = new string[] {".doc",".pdf",".docx" };

        private readonly List<string> r_FoldersToScan = new List<string>();
        private Thread m_thread;
        private int m_FolderIndex;

        private bool m_IsRunning = false;

        public event FoundFileHandler FoundFile;


        private void OnFoundFile(string pathToFile)
        {
            if (FoundFile != null)
            {
                FoundFile(pathToFile);
            }
        }

        public bool IsRunning
        {
            get { return m_IsRunning; }
        }


        public DirectoriesScanner()
        {
            ThreadPool.SetMaxThreads(MAX_THREADS, MAX_THREADS);
        }


        private void ScanDirectory(object folderToScanObj)
        {
            string folderToScan = folderToScanObj as string;
            if (folderToScan != null)
            {
                try
                {

                    foreach (string folderPath in Directory.GetDirectories(folderToScan))
                    {
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ScanDirectory), folderPath);
                    }

                    foreach (string fileName in Directory.GetFiles(folderToScan, "*.*", SearchOption.TopDirectoryOnly))
                    {
                        if (s_AllowedExtensions.Contains<string>(Path.GetExtension(fileName.ToLower())))
                        {
                            OnFoundFile(fileName);
                        }
                    }
                }
                catch (Exception ex)
                { }
            }
        }

        private void ScanDirectories()
        {
            while (this.IsRunning)
            {
                lock (this) // we cannot use foreach without lock, becuase a user might request to 
                            // remove a folder from the list - and that will cause an Exception...
                {
                    foreach (string folderToScan in r_FoldersToScan)
                    {
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ScanDirectory), folderToScan);
                    }
                }

                Thread.Sleep(MINUTE);
            }
        }

        /// <summary>
        /// start the scanner machenism
        /// </summary>
        public void Start()
        {
            if (!this.IsRunning)
            {
                m_thread = new Thread(new ThreadStart(ScanDirectories));
                m_thread.Name = "DirectoryScanThread";
                m_thread.Start();
                m_IsRunning = true;
            }
        }


        /// <summary>
        /// stop the scanner
        /// </summary>
        public void Stop()
        {
            if (this.IsRunning)
            {
                m_IsRunning = false;
                m_thread.Abort();
                m_thread = null;
                
            }

        }


        /// <summary>
        /// adds a folder to list of folders to scan
        /// </summary>
        /// <param name="Folder">a dull path to folder to scan.</param>
        public void AddFolderToScan(string Folder)
        {
            if (!Directory.Exists(Folder))
            {
                return;
            }

            r_FoldersToScan.Add(Folder);
        }

        /// <summary>
        /// removes a folder from a list of folders to scan
        /// </summary>
        /// <param name="Folder">a full path to folder to scan</param>
        /// <returns>true if the folder was succesfully removed.</returns>
        public bool RemoveFolderToScan(string Folder)
        {
            bool retVal;
            lock (this) // see comment on lock statment in ScanDirectories()
            {
                retVal = r_FoldersToScan.Remove(Folder);
            }
            return retVal;
        }

        internal void Clear()
        {
            r_FoldersToScan.Clear();
        }
    }
}
